package com.file.parser;

import com.file.bo.ResponseData;
import com.file.bo.mail.SPD;
import com.file.bo.mail.SPDTran;
import com.file.constant.ErrorCode;
import com.file.util.JsonUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.pdfbox.pdmodel.PDDocument;
import technology.tabula.*;
import technology.tabula.detectors.NurminenDetectionAlgorithm;
import technology.tabula.extractors.SpreadsheetExtractionAlgorithm;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
public class SPDPdfParser extends BaseDecryptPdfParser {

	public ResponseData<String> parseSPDPdfToJson(String filePath, String passWord) {
		log.info("parseSPDPdfToJson started, filePath:{}, passWord:{}", filePath, passWord);
		String json = null;

		try {
			SPD spd = parseSPDPdf(filePath, passWord);
			json = JsonUtils.convertObjectToJson(spd);
		} catch (Exception e) {
			log.error("[OnError]parseSPDPdfToJson failed, filePath:{}, passWord:{}", filePath, passWord, e);
			return new ResponseData<String>(json, ErrorCode.FILE_PARSE_EXCEPTION.getCode(),
					ErrorCode.FILE_PARSE_EXCEPTION.getMsg());
		}

		log.info("parseSPDPdfToJson completed, filePath:{}, passWord:{}", filePath, passWord);
		return new ResponseData<String>(json, ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg());
	}

	private SPD parseSPDPdf(String filePath, String passWord) {
		SPD spd = parseSPDHeader(filePath, passWord);

		List<SPDTran> spdTrans = parseSPDTrans(filePath, passWord);

		spd.setSpdTrans(spdTrans);

		log.info("SPD = {}", spd);
		return spd;
	}

	private SPD parseSPDHeader(String filePath, String passWord) {
		SPD spd = new SPD();
		String pdfHeaderText = parsePdfHeaderText(filePath, passWord);

		String name = pdfHeaderText.substring(pdfHeaderText.indexOf("户名:") + 3, pdfHeaderText.indexOf("账号:")).trim();
		String accountNo = pdfHeaderText.substring(pdfHeaderText.indexOf("账号:") + 3, pdfHeaderText.indexOf("起止日期:"))
				.trim();
		String startEndData = pdfHeaderText
				.substring(pdfHeaderText.indexOf("起止日期:") + 5, pdfHeaderText.indexOf("Name:")).trim();
		String currency = pdfHeaderText.substring(pdfHeaderText.indexOf("币种:") + 3, pdfHeaderText.indexOf("钞汇标志:"))
				.trim();
		String cashExchange = pdfHeaderText
				.substring(pdfHeaderText.indexOf("钞汇标志:") + 5, pdfHeaderText.indexOf("交易类型:")).trim();
		String transactionType = pdfHeaderText
				.substring(pdfHeaderText.indexOf("交易类型:") + 5, pdfHeaderText.indexOf("Currency:")).trim();

		spd.setName(name);
		spd.setAccountNo(accountNo);
		spd.setStartEndData(startEndData);
		spd.setCurrency(currency);
		spd.setCashExchange(cashExchange);
		spd.setTransactionType(transactionType);

		return spd;
	}

	private List<SPDTran> parseSPDTrans(String filePath, String passWord) {
		List<SPDTran> spdTrans = new ArrayList<>();

		// 1. 读取文件
		File pdf = new File(filePath);

		// 2. pdfbox读取PDDocument
		try (PDDocument pdfDocument = PDDocument.load(pdf, passWord)) {
			// 3. tabula新建ObjectExtractor和NurminenDetectionAlgorithm，同时准备接收表格Rectangle的结构
			ObjectExtractor objectExtractor = new ObjectExtractor(pdfDocument);
			NurminenDetectionAlgorithm detectionAlgorithm = new NurminenDetectionAlgorithm();
			Map<Integer, List<Rectangle>> detectedTables = new HashMap<>();

			// 4. 获取每页的PageIterator
			PageIterator pages = objectExtractor.extract();

			// 5. 解析每页的Rectangle(table的位置)
			while (pages.hasNext()) {
				Page page = pages.next();
				List<Rectangle> tablesOnPage = detectionAlgorithm.detect(page);
				if (tablesOnPage.size() > 0) {
					detectedTables.put(Integer.valueOf(page.getPageNumber()), tablesOnPage);
				}
			}

			// 6.通过table位置获取表格具体内容
			SpreadsheetExtractionAlgorithm bea = new SpreadsheetExtractionAlgorithm();

			// 如果单页pdf中有多个表格，则解析出来的List<Rectangle>会有多个对象
			for (Map.Entry<Integer, List<Rectangle>> entry : detectedTables.entrySet()) {
				Page page = objectExtractor.extract(entry.getKey());

				// 默认每页只有一个表格，因此获取第0个rectangle
				Rectangle rectangle = entry.getValue().get(0);
				Page area = page.getArea(rectangle.getTop(), rectangle.getLeft(), rectangle.getBottom(),
						rectangle.getRight());

				List<Table> table = bea.extract(area);
				// 默认每页只有一个表格，因此获取第0个table
				Table t = table.get(0);

				// 判断pdf是否改版，当前版本(20210927)浦发流水有8列数据
				if (t.getColCount() != 8) {
					log.error("[OnError]SPD format invalid.");
					throw new RuntimeException("SPD format invalid");
				}

				// 每一页都需要去掉表格的header
				for (int i = 0; i < t.getRowCount(); i++) {
					if (t.getCell(i, 0).getText(false).contains("交易日期")) {
						continue;
					}
					SPDTran spdTran = getTransactionByTable(t, i);
					spdTrans.add(spdTran);
				}
			}
		} catch (Exception e) {
			log.error("[OnError]parseTrans failed", e);
			throw new RuntimeException(e);
		}

		return spdTrans;
	}

	private SPDTran getTransactionByTable(Table t, int i) {
		SPDTran spdTran = new SPDTran();
		spdTran.setDate(t.getCell(i, 0).getText(false));
		spdTran.setTime(t.getCell(i, 1).getText(false));
		spdTran.setTransactionName(t.getCell(i, 2).getText(false));
		spdTran.setTransactionAmount(t.getCell(i, 3).getText(false));
		spdTran.setBalance(t.getCell(i, 4).getText(false));
		spdTran.setCounterParty(t.getCell(i, 5).getText(false));
		spdTran.setOpponentAccount(t.getCell(i, 6).getText(false));
		spdTran.setSummary(t.getCell(i, 7).getText(false));
		return spdTran;
	}

	public static void main(String[] args) {
		SPDPdfParser spdPdfParser = new SPDPdfParser();
		SPD spd = spdPdfParser.parseSPDPdf("E:\\data\\files\\SPD\\浦发银行交易流水密码281826.pdf", "281826");
		String json = JsonUtils.convertObjectToJson(spd);
		System.out.println(json);
	}

}
